package com.ibm.mongo;

import java.util.Collection;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ibm.cps.message.MessageFields;
import com.ibm.interfaces.INoSqlDB;
import com.ibm.interfaces.IPersistency;
import com.ibm.util.ErrorCode;
import com.ibm.util.JSONUtility;
import com.ibm.util.LocalConfig;
import com.ibm.util.exception.CPSException;

public class MongoPersistency implements IPersistency {

	private int metadataCol;
	final private INoSqlDB db;
	private int userCol;
	private int groupCol;
	private int dataSourceCol;
	private int publisherCol;

	private CollectionAccess colAccess;
	
	private int authCol;
	
	private static Logger logger = Logger.getLogger(MongoPersistency.class);

	private static MongoPersistency _instMongoPresistency;

	static public MongoPersistency getInstace() throws CPSException {
		if (_instMongoPresistency == null) {
			_instMongoPresistency = new MongoPersistency();
		}
		return _instMongoPresistency;
	}

	private MongoPersistency() throws CPSException {
		PropertyConfigurator.configure(LocalConfig.getLoggerProperties());
		this.db = new MongoNoSqlDB(LocalConfig.MONGO_CONNECT_STRING, LocalConfig.PERSISTENCY_DATABASE);
		logger.info("Connect to mongo persistency: " + LocalConfig.MONGO_CONNECT_STRING);

		this.userCol = db.getCollection("user");
		this.metadataCol = db.getCollection("metadata");
		this.groupCol = db.getCollection("processorGroup");
		this.dataSourceCol = db.getCollection("datasource");
		this.publisherCol = db.getCollection("publisher");
		
		this.authCol = db.getCollection("credential");
		
		colAccess = new CollectionAccess(db);
	}

	@Override
	public void putTenantStatus(String tenantid, String status) throws CPSException {
		ObjectNode userMetadata = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, userMetadata);
		checkAndPutWithException(MessageFields.STATUS, status, userMetadata);

		colAccess.put(userCol, userMetadata, MessageFields.TENANTID);
	}

	@Override
	public String getTenantStatus(String tenantid) throws CPSException {
		ObjectNode userMetadata = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, userMetadata);
		Collection<String> result = colAccess.get(userCol, userMetadata, MessageFields.TENANTID);
		if (result != null && result.size() > 1) {
			throw new CPSException(ErrorCode.INTERNAL_ERROR_CODE,
					"Only one staus for tenant " + tenantid + ", but there are " + result.size());
		}
		if (result.isEmpty()) {
			return null;
		} else {
			return result.iterator().next();
		}
	}

	@Override
	public String deleteTenantStatus(String tenantid) throws CPSException {
		ObjectNode userMetadata = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, userMetadata);
		Collection<String> result = colAccess.delete(userCol, userMetadata, MessageFields.TENANTID);
		if (result != null && result.size() > 1) {
			throw new CPSException(ErrorCode.INTERNAL_ERROR_CODE,
					"Only one staus for tenant " + tenantid + ", but there are " + result.size());
		}
		if (result.isEmpty()) {
			return null;
		} else {
			return result.iterator().next();
		}
	}

	@Override
	public void putGroupMetadata(String jsonGroupMetadata) throws CPSException {
		putGroupMetadata(JSONUtility.fromObjectString(jsonGroupMetadata));
	}

	@Override
	public void putGroupMetadata(ObjectNode object) throws CPSException {
		colAccess.put(groupCol, object, MessageFields.GROUPID);
	}

	@Override
	public Collection<String> getGroupMetadata(String tenantid, String groupid) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.GROUPID, groupid, filter);
		return colAccess.get(groupCol, filter, MessageFields.GROUPID);
	}

	@Override
	public Collection<String> getAllGroupMetadatas() throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		return colAccess.get(groupCol, filter, MessageFields.GROUPID);
	}

	@Override
	public Collection<String> deleteGroupMetadata(String tenantid, String groupid) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.GROUPID, groupid, filter);
		return colAccess.delete(groupCol, filter, MessageFields.GROUPID);
	}

	@Override
	public void putDataSource(String jsonDataSourceMetadata) throws CPSException {
		putDataSource(JSONUtility.fromObjectString(jsonDataSourceMetadata));
	}

	@Override
	public void putDataSource(ObjectNode object) throws CPSException {
		colAccess.put(dataSourceCol, object, MessageFields.NAME);
	}

	@Override
	public Collection<String> getDataSource(String tenantid, String dataSourceName) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithNoException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.NAME, dataSourceName, filter);
		return colAccess.get(dataSourceCol, filter, MessageFields.NAME);
	}

	@Override
	public Collection<String> getTenantDataSource(String tenantid, String dataSourceName) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.NAME, dataSourceName, filter);
		return colAccess.get(dataSourceCol, filter, MessageFields.NAME);
	}

	@Override
	public String getDataSourceByName(String tenantid, String dataSourceName) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithException(MessageFields.NAME, dataSourceName, filter);
		Collection<String> result = colAccess.get(dataSourceCol, filter, MessageFields.NAME);
		if (result == null || result.size() < 1) {
			return null;
		} else {
			return result.iterator().next();
		}
	}

	@Override
	public Collection<String> deleteDataSource(String tenantid, String dataSourceName) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.NAME, dataSourceName, filter);
		return colAccess.delete(dataSourceCol, filter, MessageFields.NAME);
	}

	@Override
	public void putPublisher(String jsonMetadata) throws CPSException {
		putPublisher(JSONUtility.fromObjectString(jsonMetadata));
	}

	@Override
	public void putPublisher(ObjectNode jsonMetadata) throws CPSException {
		colAccess.put(publisherCol, jsonMetadata, MessageFields.NAME);
	}

	@Override
	public Collection<String> getPublisher(String tenantid, String name) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithNoException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.NAME, name, filter);
		return colAccess.get(publisherCol, filter, MessageFields.NAME);
	}

	@Override
	public Collection<String> deletePublisher(String tenantid, String publisherName) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.NAME, publisherName, filter);
		return colAccess.delete(publisherCol, filter, MessageFields.NAME);
	}

	@Override
	public void putProcessor(String jsonProcessorMetadata) throws CPSException {
		putProcessor(JSONUtility.fromObjectString(jsonProcessorMetadata));
	}

	@Override
	public void putProcessor(ObjectNode object) throws CPSException {
		colAccess.put(metadataCol, object, MessageFields.PROCESSORID);
	}

	@Override
	public Collection<String> getProcessors(String tenantid, String processorid, String processorType, String groupId)
			throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithNoException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.PROCESSORID, processorid, filter);
		checkAndPutWithNoException(MessageFields.TYPE, processorType, filter);
		checkAndPutWithNoException(MessageFields.GROUPID, groupId, filter);
		return colAccess.get(metadataCol, filter, MessageFields.PROCESSORID);
	}

	@Override
	public Collection<String> getProcessors(ObjectNode filter) throws CPSException {
		return colAccess.get(metadataCol, filter, MessageFields.PROCESSORID);
	}
	
	@Override
	public Collection<String> getProcessorGroups(ObjectNode filter) throws CPSException{
		return colAccess.get(groupCol, filter, "_id");
	}

	@Override
	public Collection<String> deleteProcessors(String tenantid, String processorid, String processorType,
			String groupId) throws CPSException {
		ObjectNode filter = JSONUtility.newObjectNode();
		checkAndPutWithException(MessageFields.TENANTID, tenantid, filter);
		checkAndPutWithNoException(MessageFields.PROCESSORID, processorid, filter);
		checkAndPutWithNoException(MessageFields.TYPE, processorType, filter);
		checkAndPutWithNoException(MessageFields.GROUPID, groupId, filter);
		return colAccess.delete(metadataCol, filter, MessageFields.PROCESSORID);
	}

	private void checkAndPutWithException(String key, String value, ObjectNode doc) throws CPSException {
		if (value == null) {
			throw new CPSException(ErrorCode.ERROR_FIELD_TYPE, "The value of field " + key + " is null.");
		}
		doc.put(key, value);
	}

	private void checkAndPutWithNoException(String key, String value, ObjectNode doc) {
		if (value != null) {
			doc.put(key, value);
		}
	}

	@Override
	public Collection<String> getTenantDataSource(ObjectNode filter) throws CPSException {
		return colAccess.get(dataSourceCol, filter, MessageFields.NAME);
	}

	@Override
	public Collection<String> getPublisher(ObjectNode filter) throws CPSException {
		return colAccess.get(publisherCol, filter, MessageFields.NAME);
	}

	@Override
	public Collection<String> getCredential(ObjectNode filter) throws CPSException {
		return colAccess.get(authCol, filter,  MessageFields.TENANTID);
	}

	@Override
	public void postCredential(ObjectNode filter) throws CPSException {
		colAccess.post(authCol, filter, MessageFields.TENANTID);
	}

	@Override
	public Collection<String> deleteCredential(ObjectNode filter) throws CPSException {
		return colAccess.delete(authCol, filter,  MessageFields.TENANTID);
	}


}
